package com.skyline.energy.dataaccess.jdbc;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.skyline.energy.datasource.DistributeDataSource;
import com.skyline.energy.exception.TransactionException;
import com.skyline.energy.transaction.TransactionContext;
import com.skyline.energy.transaction.TransactionContextHolder;
import com.skyline.energy.transaction.TransactionDefinition;
import com.skyline.energy.transaction.TransactionManager;
import com.skyline.energy.utils.JdbcUtils;

public class JdbcTransactionManager implements TransactionManager {
	private static final Log LOGGER = LogFactory.getLog(JdbcTransactionManager.class);
	private DataSource dataSource;
	private boolean lazyBegin = false;

	public boolean isLazyBegin() {
		//DistributeDataSource also need lazyBegin
		return (lazyBegin || (dataSource instanceof DistributeDataSource));
	}

	public void setLazyBegin(boolean lazyBegin) {
		this.lazyBegin = lazyBegin;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	@Override
	public TransactionContext begin(TransactionDefinition definition) {
		JdbcTransactionContext context = (JdbcTransactionContext)TransactionContextHolder.getContext();

		if (context == null) {
			context = new JdbcTransactionContext(this);
			context.setCurrentDefinition(definition);
		}

		ConnectionHolder conHolder = context.getConnectionHolder();
		//已经存在事务,或者不是缓启动
		if (!isLazyBegin() || conHolder == null || conHolder.getCurrentConnection() == null) {
			doBegin(context, definition);
		}
		
		return context;
	}

	@Override
	public void commit(TransactionContext context) {
		if (context.isRollbackOnly() || context.isComplete()) {//context.isComplete()为已经回滚的数据
			rollback(context);
			return;
		}

		try {
			JdbcTransactionContext txObject = (JdbcTransactionContext) context;
			JdbcUtils.commitTransaction(txObject.getConnectionHolder().getCurrentConnection());
		} catch (SQLException e) {
			throw new TransactionException("Commit transaction failed.", e);
		} catch (RuntimeException e) {
			throw new TransactionException("Commit transaction failed.", e);
		} finally {
			clean(context);//清除事务
		}
	}

	@Override
	public void rollback(TransactionContext context) {
		if (context.hasRollback()) {
			return;
		}

		context.setHasRollback(true);

		JdbcTransactionContext txObject = (JdbcTransactionContext) context;
		try {
			JdbcUtils.rollbackTransaction(txObject.getConnectionHolder().getCurrentConnection());
		} catch (SQLException e) {
			throw new TransactionException("Rollback transaction failed.", e);
		} catch (RuntimeException e) {
			throw new TransactionException("Rollback transaction failed.", e);
		} finally {
			txObject.setComplete(true);
			txObject.getConnectionHolder().setCurrentConnection(null);//清除链接
		}
	}

	private void clean(TransactionContext context) {
		TransactionContextHolder.setContext(null);
	}

	private void doBegin(TransactionContext context, TransactionDefinition definition) {
		JdbcTransactionContext txObject = (JdbcTransactionContext) context;
		Connection con = null;
		ConnectionHolder conHolder = txObject.getConnectionHolder();
		
		try {
			if (conHolder == null && !isLazyBegin()) {
				Connection newCon = JdbcUtils.getConnection(dataSource);
				LOGGER.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				conHolder = new ConnectionHolder(newCon);
				txObject.setConnectionHolder(conHolder);
				txObject.setTxDataSource(dataSource);
			} else if (conHolder == null) {
				throw new TransactionException("lazy begin must has a connection holder");
			}
			
			con = conHolder.getCurrentConnection();
			JdbcUtils.prepareConnectionForTransaction(con, definition);
	
		} catch (Exception ex) {
			JdbcUtils.releaseConnection(con);
			throw new TransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}
}
